package org.bjf.utils;

import com.github.rholder.retry.*;
import com.google.common.base.Predicates;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;


/**
 * @author bjf
 */
@Slf4j
public class RetryUtil {

    /**
     * 默认重试机制
     */
    public static <T> Retryer<T> getDefaultRetryer() {

        return RetryerBuilder.<T>newBuilder()
                //抛出runtime异常、checked异常时都会重试，但是抛出error不会重试。
                .retryIfException()
                //尝试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                //重试策略 时间间隔3秒，随着重试次数的增加每次递增 1 s
                .withWaitStrategy(
                        WaitStrategies.incrementingWait(3, TimeUnit.SECONDS, 1, TimeUnit.SECONDS))
                .withRetryListener(new RetryListener() {
                    @Override
                    public <V> void onRetry(Attempt<V> attempt) {
                        log.debug("retry time = " + attempt.getAttemptNumber());
                        if (attempt.hasException()) {
                            log.error("try failed", attempt.getExceptionCause());
                        }
                    }
                })
                .build();
    }

    /**
     * @param times 重试次数
     * @param delay 重新时间间隔
     */
    public static <T> Retryer<T> getRetryer(int times, int delay) {

        return RetryerBuilder.<T>newBuilder()
                //抛出runtime异常、checked异常时都会重试，但是抛出error不会重试。
                .retryIfException()
                //尝试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(times))
                //重试策略 固定时间间隔3秒
                .withWaitStrategy(WaitStrategies.fixedWait(delay, TimeUnit.SECONDS))
                //重试策略 时间间隔3秒，随着重试次数的增加每次递增 1 s
                //.withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS, 1, TimeUnit.SECONDS))
                .withRetryListener(new RetryListener() {
                    @Override
                    public <V> void onRetry(Attempt<V> attempt) {
                        log.debug("retry time = " + attempt.getAttemptNumber());
                        if (attempt.hasException()) {
                            log.error("try failed", attempt.getExceptionCause());
                        }
                    }
                })
                .build();
    }

    /**
     * @param times    重试次数
     * @param delay    重新时间间隔
     * @param timeUnit 时间单元
     */
    public static <T> Retryer<T> getRetryer(int times, int delay, TimeUnit timeUnit) {

        return RetryerBuilder.<T>newBuilder()
                //抛出runtime异常、checked异常时都会重试，但是抛出error不会重试。
                .retryIfException()
                //尝试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(times))
                //重试策略 固定时间间隔3秒
                .withWaitStrategy(WaitStrategies.fixedWait(delay, timeUnit))
                //重试策略 时间间隔3秒，随着重试次数的增加每次递增 1 s
                //.withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS, 1, TimeUnit.SECONDS))
                .withRetryListener(new RetryListener() {
                    @Override
                    public <V> void onRetry(Attempt<V> attempt) {
                        log.debug("retry time = " + attempt.getAttemptNumber());
                        if (attempt.hasException()) {
                            log.error("try failed", attempt.getExceptionCause());
                        }
                    }
                })
                .build();
    }

    /**
     * @param retryValue 指定重试的值
     * @param times      重试次数
     * @param delay      重新时间间隔
     */
    public static <T> Retryer<T> getRetryer(T retryValue, int times, int delay) {

        return RetryerBuilder.<T>newBuilder()
                //抛出runtime异常、checked异常时都会重试，但是抛出error不会重试。
                .retryIfException()
                //返回指定值也需要重试
                .retryIfResult(Predicates.equalTo(retryValue))
                //尝试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(times))
                //重试策略 固定时间间隔3秒
                .withWaitStrategy(WaitStrategies.fixedWait(delay, TimeUnit.SECONDS))
                //重试策略 时间间隔3秒，随着重试次数的增加每次递增 1 s
                //.withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS, 1, TimeUnit.SECONDS))
                .withRetryListener(new RetryListener() {
                    @Override
                    public <V> void onRetry(Attempt<V> attempt) {
                        log.debug("retry time = " + attempt.getAttemptNumber());
                        if (attempt.hasException()) {
                            log.error("try failed", attempt.getExceptionCause());
                        }
                    }
                })
                .build();
    }

    public static void main(String[] args) {
        try {
            RetryUtil.getRetryer(0L, 3, 3)
                    .call(() -> {
                        Long val = 99L;
                        return val;
                    });
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        // spring retry 解决方案
    }

}